import { assertEquals } from "@std/assert";
import { buildQueryObj } from "./main.ts";

interface UserDTO {
  name: string;
  age: number;
  email: string;
  address: string;
}

Deno.test("test eq", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "name",
        op: "eq",
        value: "test",
      },
    ],
  });
  assertEquals(query, {
    "name-op": "eq",
    "name": "test",
  });
});

Deno.test("test eq url encode", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "name",
        op: "eq",
        value: "test|",
      },
    ],
  });
  assertEquals(query, { "name-op": "eq", name: "test%7C" });
});

Deno.test("test eq unicode value", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "name",
        op: "eq",
        value: "南通",
      },
    ],
  });
  assertEquals(query, { "name-op": "eq", name: "%E5%8D%97%E9%80%9A" });
});

Deno.test("test ne", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "name",
        op: "ne",
        value: "test",
      },
    ],
  });
  assertEquals(query, { "name-op": "ne", name: "test" });
});

Deno.test("test gt", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "age",
        op: "gt",
        value: 18,
      },
    ],
  });
  assertEquals(query, { "age-op": "gt", age: "18" });
});

Deno.test("test ge", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "age",
        op: "ge",
        value: 18,
      },
    ],
  });
  assertEquals(query, { "age-op": "ge", age: "18" });
});

Deno.test("test lt", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "age",
        op: "lt",
        value: 18,
      },
      {
        label: "name",
        op: "ct",
        value: "john",
      },
    ],
  });
  assertEquals(query, {
    "age-op": "lt",
    age: "18",
    "name-op": "ct",
    name: "john",
  });
});

Deno.test("test le", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "age",
        op: "le",
        value: 18,
      },
    ],
  });
  assertEquals(query, { "age-op": "le", age: "18" });
});

Deno.test("test bt", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "age",
        op: "bt",
        value: [18, 30],
      },
    ],
  });
  assertEquals(query, { "age-op": "bt", "age-0": "18", "age-1": "30" });
});

Deno.test("test bt url encode", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "age",
        op: "bt",
        value: ["18&", "30|"],
      },
    ],
  });
  assertEquals(query, { "age-op": "bt", "age-0": "18%26", "age-1": "30%7C" });
});

Deno.test("test nb", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "age",
        op: "nb",
        value: [18, 30],
      },
    ],
  });
  assertEquals(query, { "age-op": "nb", "age-0": "18", "age-1": "30" });
});

Deno.test("test sw", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "name",
        op: "sw",
        value: "John",
      },
    ],
  });
  assertEquals(query, { "name-op": "sw", name: "John" });
});

Deno.test("test ew", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "email",
        op: "ew",
        value: "example.com",
      },
    ],
  });
  assertEquals(query, { "email-op": "ew", email: "example.com" });
});

Deno.test("test ol", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "name",
        op: "ol",
        value: ["John", "Jane", "Bob"],
      },
    ],
  });
  assertEquals(query, {
    "name-op": "ol",
    "name-0": "John",
    "name-1": "Jane",
    "name-2": "Bob",
  });
});

Deno.test("test ol url encode", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "name",
        op: "ol",
        value: ["John@", "Jane&", "Bob|"],
      },
    ],
  });
  assertEquals(query, {
    "name-op": "ol",
    "name-0": "John%40",
    "name-1": "Jane%26",
    "name-2": "Bob%7C",
  });
});

Deno.test("test nk", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "name",
        op: "nk",
        value: "stranger",
      },
    ],
  });
  assertEquals(query, { "name-op": "nk", name: "stranger" });
});

Deno.test("test il", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "age",
        op: "il",
        value: [18, 25, 30],
      },
    ],
  });
  assertEquals(query, {
    "age-op": "il",
    "age-0": "18",
    "age-1": "25",
    "age-2": "30",
  });
});

Deno.test("test ni", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "age",
        op: "ni",
        value: [18, 25, 30],
      },
    ],
  });
  assertEquals(query, {
    "age-op": "ni",
    "age-0": "18",
    "age-1": "25",
    "age-2": "30",
  });
});

Deno.test("test nl", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "email",
        op: "nl",
      },
    ],
  });
  assertEquals(query, {
    "email-op": "nl",
  });
});

Deno.test("test nn", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "email",
        op: "nn",
      },
    ],
  });
  assertEquals(query, {
    "email-op": "nn",
  });
});

Deno.test("test ey", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "address",
        op: "ey",
      },
    ],
  });
  assertEquals(query, {
    "address-op": "ey",
  });
});

Deno.test("test ny", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "address",
        op: "ny",
      },
    ],
  });
  assertEquals(query, {
    "address-op": "ny",
  });
});

Deno.test("test at", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "name",
        op: "at",
      },
    ],
  });
  assertEquals(query, {
    "name-op": "at",
  });
});

Deno.test("test af", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "name",
        op: "af",
      },
    ],
  });
  assertEquals(query, {
    "name-op": "af",
  });
});

Deno.test("more than one field", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [
      {
        label: "name",
        op: "eq",
        value: "test",
      },
      {
        label: "age",
        op: "gt",
        value: 18,
      },
    ],
  });
  assertEquals(query, {
    "name-op": "eq",
    "name": "test",
    "age-op": "gt",
    "age": "18",
  });
});

Deno.test("fields params and group param", () => {
  const query = buildQueryObj<UserDTO>({
    fields: {
      A: [
        {
          label: "name",
          op: "eq",
          value: "test",
        },
        {
          label: "age",
          op: "gt",
          value: 18,
        },
      ],
      B: [
        {
          label: "address",
          op: "ct",
          value: "test",
        },
      ],
    },
    gexpr: "A|B",
  });
  assertEquals(
    query,
    {
      "A.name-op": "eq",
      "A.name": "test",
      "A.age-op": "gt",
      "A.age": "18",
      "B.address-op": "ct",
      "B.address": "test",
      "gexpr": "A%7CB",
    },
  );
});

Deno.test("test tuple fields", () => {
  const query = buildQueryObj<UserDTO>({
    fields: [["name", "eq", "test"]],
  });
  assertEquals(query, {
    "name-op": "eq",
    "name": "test",
  });
});

Deno.test("fields is undefined", () => {
  const query = buildQueryObj<UserDTO>({
    page: 1,
    size: 100,
  });
  assertEquals(query, {
    "page": "1",
    "size": "100",
  });
});

Deno.test("test orderBy", () => {
  const query = buildQueryObj<UserDTO>({
    orderBy: [{ label: "name", order: "asc" }, { label: "age", order: "desc" }],
  });
  assertEquals(query, {
    "orderBy": "name:asc,age:desc",
  });
});

Deno.test("orderBy extra generic", () => {
  const query = buildQueryObj<UserDTO, "id">({
    orderBy: [{ label: "name", order: "asc" }, { label: "id", order: "desc" }],
  });
  assertEquals(query, {
    "orderBy": "name:asc,id:desc",
  });
});
